﻿/************************************************************************************ 
 * SceneObject.cs
 * 
 * Author      : Hristo Hristov
 * Datum       : 21.05.2011
 * Version     : 1.0
 * Revision    : 25
 * Description : Abstract superclass of a NOVA scene object, which contains the scene 
 *               object "TransformNode", "GeometryNode", "ParticleNode", "IShader" from 
 *               GoblinXNA and a physics object specific for the BEPU physics engine. 
 *               Furthermore it also contains a particle and shader specification.
 *               
 *               
 * UPDATE (27.06.11): - Distinction between Translate/Rotate methods, which transform
 *                      the object using the physics engine and the Position/Orientation
 *                      method, which assign the values to the object directly without
 *                      carrying about the physical effect. I.e: the "Translation" method
 *                      would "move" the object to the specified position and "Position"
 *                      method would "teleport" the object.
 * 
 *************************************************************************************/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NOVA.Physics;
using Microsoft.Xna.Framework;
using BEPUphysics.Entities;
using Microsoft.Xna.Framework.Graphics;
using BEPUphysics.Paths;
using BEPUphysics.DataStructures;
using BEPUphysics.Collidables;
using BEPUphysics.MathExtensions;
using BEPUphysics.CollisionShapes.ConvexShapes;
using BEPUphysics.Entities.Prefabs;
using BEPUphysics;
using Nuclex.Fonts;
using System.IO;
using BEPUphysics.CollisionShapes;
using NOVA.Utilities;

using DebugShapeRenderer = NOVA.Utilities.DebugShapeRenderer;
using NOVA.Scenery;
using NOVA.Graphics;
using BEPUphysics.UpdateableSystems;
using BEPUphysics.Collidables.MobileCollidables;
using BEPUphysics.NarrowPhaseSystems.Pairs;
using BEPUphysics.Collidables.Events;
using BEPUphysics.CollisionTests;

namespace NOVA.Scenery
{
    /// <summary>
    /// Render delegate, which defines the rendering of the object.
    /// </summary>
    public delegate void RenderHandler();

    public class CollisionArgs : EventArgs
    {
        private SceneObject collider;
        private Contact contact;

        public SceneObject Collider
        {
            get { return this.collider;  }
            set { this.collider = value; }
        }

        public Contact Contact
        {
            get { return this.contact; }
            set { this.contact = value; }
        }
    }

    /// <summary>
    /// This class is used as a base class for all scene objects
    /// </summary>
    public class SceneObject : ITransformable
    {
        #region Members

        private Scene scene;

        private object tag = null;

        private event EventHandler<CollisionArgs> collided;

        internal MotorizedGrabSpring m_grabber;
        internal Vector3 m_lastPosition;
        internal Quaternion m_lastOrientation;
        MotionSetting m_motionSetting;
        public BoundingBox m_grabberBoundingBox;
        bool m_bPositionSet;
        bool m_bOrientationSet;

        internal bool m_bPositionProcessed;
        internal bool m_bOrientationProcessed;

        private Matrix m_controllerMatrix;
        private bool m_bHasController;

        public Vector3 m_graphicsPhysOffset;

        internal Vector3 m_movePosition;
        internal Quaternion m_rotateOrientation;

        int m_iID;

        protected Vector3 m_position;
        protected Vector3 m_scale;
        protected Quaternion m_rotation;
        internal Quaternion m_preRotation;
        private string m_strName;

        // GoblinXNA objects
        internal IModel m_model;
        //internal ParticleNode m_particleNode;
        //internal UnifiedShader m_shader;
        internal RenderMaterial m_renderMat;

        // BEPU physics objects
        internal Entity m_physicsObject;
        internal BEPUphysics.Materials.Material m_physMat;

        private string m_strType;

        private string m_shapeParameters;
        
        private Matrix m_worldMatrix;

        private bool m_bVisible;

        private bool m_bShowWireframe;
        private Scenery.RenderType renderType;
        private RenderHandler renderDelegate;
        private bool collidedAssigned = false;
        private Quaternion localOrientation;
        private bool useLocalOrientation = true;

        #endregion

        #region Properties

        public object Tag
        {
            get { return this.tag; }
            set { this.tag = value; }
        }

        internal bool UseLocalOrientation
        {
            get { return this.useLocalOrientation; }
            set { this.useLocalOrientation = value; }
        }

        public Scene Scene
        {
            get 
            { 
                return this.scene; 
            }
            internal set 
            { 
                this.scene = value;
            }
        }

        public Vector3 GraphicsPhysicsOffset
        {
            get { return m_graphicsPhysOffset; }
            set { m_graphicsPhysOffset = value; }
        }

        /// <summary>
        /// If enabled, the wireframes of object is rendered.
        /// </summary>
        public bool ShowWireframe
        {
            get { return m_bShowWireframe; }
            set { m_bShowWireframe = true; }
        }

        /// <summary>
        /// Gets or sets the visibility property of the object. 
        /// </summary>
        public bool Visible
        {
            get { return m_bVisible; }
            set { m_bVisible = value; }
        }

        /// <summary>
        /// Gets or sets the motion setting of this object.
        /// </summary>
        public MotionSetting MotionSetting
        {
            get { return m_motionSetting; }
            set { m_motionSetting = value; }
        }

        /// <summary>
        /// Gets the grabber instance
        /// </summary>
        public MotorizedGrabSpring Grabber
        {
            get { return m_grabber; }
        }

        /// <summary>
        /// Gets or sets the object's physical mass and changes 
        /// the kinetic/dynamic state of the object, if needed.
        /// </summary>
        public float Mass
        {
            get { return m_physicsObject.Mass; }
            set
            {
                // If the physics object is static and new mass is non-zero, make it dynamic
                if (!m_physicsObject.IsDynamic && value > 0)
                    m_physicsObject.BecomeDynamic(value);
                else // If the physics object is dynamic and new mass is zero, make it kinematic
                    if (m_physicsObject.IsDynamic && value == 0)
                        m_physicsObject.BecomeKinematic();

                m_physicsObject.Mass = value;
            }
        }

        /// <summary>
        /// Gets or sets the render type.
        /// </summary>
        internal RenderType RenderType
        {
            get { return this.renderType; }
            set { this.renderType = value; }
        }

        public Matrix ControllerMatrix
        {
            get { return m_controllerMatrix; }
            set { m_controllerMatrix = value; }
        }

        public bool HasController
        {
            get { return m_bHasController; }
            set { m_bHasController = value; }
        }

        /// <summary>
        /// Gets or sets the render delegate. Use set with caution!
        /// </summary>
        public RenderHandler RenderDelegate 
        {
            get { return this.renderDelegate; }
            set { this.renderDelegate = value; }
        }

        public Quaternion Orientation
        {
            get
            {
                return m_physicsObject.Orientation;
            }
            set
            {
                if (this.useLocalOrientation)
                {
                    this.localOrientation = value;
                }

                m_physicsObject.Orientation = value;
            }
        }

        internal Quaternion LocalOrientation 
        {
            get { return this.localOrientation; }
        }

        public Matrix WorldMatrix
        {
            get { return m_worldMatrix; }
            set { m_worldMatrix = value; }
        }

        public int ID { get { return m_iID; } }

        public string Name 
        {
            get { return m_strName + this.ID.ToString(); }
            set { m_strName = value; }
        }

        /// <summary>
        /// Gets the shape parameters.
        /// </summary>
        public string ShapeParameters
        {
            get { return m_shapeParameters; }
            internal set { m_shapeParameters = value; }
        }

        /// <summary>
        /// Gets or sets the object type
        /// </summary>
        public string Type
        {
            get { return m_strType; }
            set { m_strType = value; }
        }

        public virtual BoundingBox BoundingBox
        {
            get
            {
                Vector3 pos = m_physicsObject.Position;                
                BoundingBox box = new BoundingBox(Model.MinimumBoundingBox.Min + pos, Model.MinimumBoundingBox.Max + pos);
                return box;// Model.MinimumBoundingBox; 
            }
        }

        public BoundingSphere BoundingSphere
        {
            get { return Model.MinimumBoundingSphere; }
        }

        public virtual Vector3 Scale
        {            
            get { return m_scale; }
            set
            {
                m_scale = value;

                // Scale the physical object
                //m_physicsObject.WorldTransform *= Matrix.CreateScale(m_scale);
            }
        }

        public IModel Model
        {
            get { return m_model; }
            set { m_model = value; }
        }

        /// <summary>
        /// Gets or sets the physical object. Use set with caution!
        /// </summary>
        public Entity Physics
        {
            get { return m_physicsObject; }
            set { m_physicsObject = value; }
        }

        public Quaternion Rotation
        {
            get
            {
                return m_rotation;//m_physicsObject.Rotation; 
            }
            set
            {
                //m_physicsObject.Rotation = (value * m_physicsObject.Rotation); 
                m_rotation = value;
            }
        }

        /// <summary>
        /// Gets or sets the position. Use set with caution! 
        /// This woudl teleport the object to the new position 
        /// directly without carring about physics!
        /// </summary>
        public Vector3 Position
        {
            //get { return m_physicsObject.Body.Position; }
            get
            {
                //return m_geometryNode.WorldTransformation.Translation; 
                return m_physicsObject.Position;//m_position;
            }
            set
            {
                m_position = value;
                m_lastPosition = value;

                if (m_physicsObject != null)
                    m_physicsObject.Position = value;
            }
        }

        /// <summary>
        /// Gets or sets the render material
        /// </summary>
        public RenderMaterial RenderMaterial
        {
            get { return m_renderMat; }
            set
            {
                m_renderMat = value;
            }
        }

        /// <summary>
        /// Gets or sets the physical materail
        /// </summary>
        public BEPUphysics.Materials.Material PhysicsMaterial
        {
            get { return m_physicsObject.Material; }
            set
            {
                m_physMat = value;
                m_physicsObject.Material = m_physMat;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// This class is used as a base class for all scene objects
        /// </summary>
        public SceneObject()
        {
            // Create a default random-colored material
            var random = new Random((int)DateTime.Now.Ticks);
            m_renderMat = new RenderMaterial();
            m_renderMat.Diffuse = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), 1.0f);
            m_renderMat.Specular = Color.White.ToVector4();
            m_renderMat.Ambient = Color.White.ToVector4();
            m_renderMat.Emissive = Color.White.ToVector4();
            m_renderMat.SpecularPower = 100f;

            //m_shader = new UnifiedShader();

            m_position = Vector3.Zero;
            m_lastPosition = Vector3.One * 1000;
            m_rotation = Quaternion.Identity;
            m_scale = Vector3.One;

            m_grabber = new MotorizedGrabSpring();            
            m_grabberBoundingBox = new Microsoft.Xna.Framework.BoundingBox();

            //m_particleNode = new ParticleNode();

            m_physMat = new BEPUphysics.Materials.Material();

            m_strName = "";

            m_bPositionSet = false;
            m_bOrientationSet = false;

            m_bVisible = true;

            m_motionSetting = MotionSetting.HardSetting;//SoftSetting;

            m_graphicsPhysOffset = Vector3.Zero;

            m_controllerMatrix = Matrix.Identity;

            m_iID = Core.GetNextNodeID();

            RenderDelegate = delegate
            {
                try
                {
                    if (m_model is PrimitiveModel)
                    {
                        Core.Device.SetVertexBuffer(((PrimitiveModel)m_model).CustomMesh.VertexBuffer);
                        Core.Device.Indices = ((PrimitiveModel)m_model).CustomMesh.IndexBuffer;
                        Core.Device.DrawIndexedPrimitives(((PrimitiveModel)m_model).CustomMesh.PrimitiveType,
                            0, 0, ((PrimitiveModel)m_model).CustomMesh.NumberOfVertices, 0, ((PrimitiveModel)m_model).CustomMesh.NumberOfPrimitives);
                    }
                    else if (m_model is MeshModel)
                    {
                        foreach (ModelMesh mesh in ((MeshModel)m_model).Mesh)
                        {
                            foreach (ModelMeshPart part in mesh.MeshParts)
                            {
                                //part.Effect.CurrentTechnique.Passes[0].Apply();

                                Core.Device.SetVertexBuffer(part.VertexBuffer);
                                Core.Device.Indices = part.IndexBuffer;
                                Core.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                    part.VertexOffset, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
                            }
                        }
                    }
                }
                catch (Exception exp)
                {
                    ExceptionManager.Add(exp);
                }
            };
        }
        #endregion

        #region Events

        public event EventHandler<CollisionArgs> Collided
        {
            add 
            { 
                this.collided += value;

                if (!this.collidedAssigned) // To avoid multiple event triggering
                {
                    this.m_physicsObject.CollisionInformation.Events.CreatingContact += new CreatingContactEventHandler<EntityCollidable>(Events_CreatingContact);
                }

                this.collidedAssigned = true;
            }

            remove
            {
                this.collided -= value;

                if (this.collidedAssigned) // To avoid multiple event triggering
                {
                    this.m_physicsObject.CollisionInformation.Events.CreatingContact -= new CreatingContactEventHandler<EntityCollidable>(Events_CreatingContact);
                }

                this.collidedAssigned = false;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Moves the object to the given position using physics.
        /// </summary>
        public void MoveToPosition(Vector3 position)
        {
            //if (!m_grabber.IsGrabbing)
            //{
            //    m_grabber.SetupLinear(m_physicsObject, m_physicsObject.Position, m_motionSetting);//true);
            //}

            //if (position != m_grabber.GoalPosition)
            //{
            //    //m_grabberBoundingBox = new Microsoft.Xna.Framework.BoundingBox(Model.MinimumBoundingBox.Min * 1.1f /*+ position*/, 1.1f * Model.MinimumBoundingBox.Max /*+ position*/);
            //    m_grabber.GoalPosition = position;
            //}

            m_movePosition = position;

            m_bPositionProcessed = true;

            m_bPositionSet = true;
        }

        internal void MovingPosition(Vector3 position)
        {
            if (!m_grabber.IsGrabbing)
            {
                m_grabber.SetupLinear(m_physicsObject, m_physicsObject.Position, m_motionSetting);//true);
            }

            if (position != m_grabber.GoalPosition)
            {
                //m_grabberBoundingBox = new Microsoft.Xna.Framework.BoundingBox(Model.MinimumBoundingBox.Min * 1.1f /*+ position*/, 1.1f * Model.MinimumBoundingBox.Max /*+ position*/);
                m_grabber.GoalPosition = position;
            }

            m_bPositionSet = true;
        }

        /// <summary>
        /// Rotates the object to the given orientation using physics.
        /// </summary>
        public void RotateTo(Quaternion rotation)
        {
            //if (!m_grabber.IsGrabbing)
            //{
            //    m_grabber.SetupAngular(m_physicsObject, rotation/*m_physicsObject.Position*/, m_motionSetting);
            //}

            //if (rotation != m_grabber.GoalOrientation)
            //{
            //    //m_grabberBoundingBox = new Microsoft.Xna.Framework.BoundingBox(Model.MinimumBoundingBox.Min * 1.1f + m_physicsObject.Position, 1.1f * Model.MinimumBoundingBox.Max + m_physicsObject.Position);
            //    m_grabber.GoalOrientation = rotation;
            //}

            m_rotateOrientation = rotation;

            m_bOrientationProcessed = true;
            
            m_bOrientationSet = true;
        }

        internal void RotatingTo(Quaternion rotation)
        {
            if (!m_grabber.IsGrabbing)
            {
                m_grabber.SetupAngular(m_physicsObject, rotation/*m_physicsObject.Position*/, m_motionSetting);
            }

            if (rotation != m_grabber.GoalOrientation)
            {
                //m_grabberBoundingBox = new Microsoft.Xna.Framework.BoundingBox(Model.MinimumBoundingBox.Min * 1.1f + m_physicsObject.Position, 1.1f * Model.MinimumBoundingBox.Max + m_physicsObject.Position);
                m_grabber.GoalOrientation = rotation;
            }

            m_bOrientationSet = true;
        }

        internal void TransformTo(Vector3 position, Quaternion quad)
        {
            if (!m_grabber.IsGrabbing)
            {
                m_grabber.Setup(m_physicsObject, m_physicsObject.Position, m_motionSetting);//true);                            
            }

            m_grabber.GoalPosition = position;
            m_grabber.GoalOrientation = quad;
        }

        /// <summary>
        /// Check it the entity is currently moving. If so, then check if it has reached it's goal
        /// with two decimal point accuracy. In this case, stop the movement by releasing the entity 
        /// from the grabber.
        /// </summary>
        public void ValidateMovement()
        {
            if (m_grabber.IsGrabbing)
            {
                //bool bShouldRelease = false;

                //if (((int)(Grabber.GrabbedPosition.X * 100)) == ((int)(Grabber.GoalPosition.X * 100)) &&
                //    ((int)(Grabber.GrabbedPosition.Y * 100)) == ((int)(Grabber.GoalPosition.Y * 100)) &&
                //    ((int)(Grabber.GrabbedPosition.Z * 100)) == ((int)(Grabber.GoalPosition.Z * 100)))
                //    m_grabber.Release();
                //if (m_grabberBoundingBox.Contains(new BoundingBox(Model.MinimumBoundingBox.Min + m_grabber.GrabbedPosition, Model.MinimumBoundingBox.Max + m_grabber.GrabbedPosition)) == ContainmentType.Contains)
                //if (m_grabber.GrabbedPosition.X * 0.95f < m_grabber.GoalPosition.X && m_grabber.GrabbedPosition.X * 1.05f > m_grabber.GoalPosition.X &&
                //    m_grabber.GrabbedPosition.Y * 0.95f < m_grabber.GoalPosition.Y && m_grabber.GrabbedPosition.Y * 1.05f > m_grabber.GoalPosition.Y &&
                //    m_grabber.GrabbedPosition.Z * 0.95f < m_grabber.GoalPosition.Z && m_grabber.GrabbedPosition.Z * 1.05f > m_grabber.GoalPosition.Z)
                
                // If the object hasn't moved (because the path is maybe blocked by other objects), release it early than usual
                if (m_bPositionSet)
                {
                    //if (m_physicsObject.Position == m_lastPosition)
                    //{
                    //    m_grabber.Release();
                    //    m_bPositionSet = false;
                    //}
                    //else
                    {
                        //if (Math.Abs(m_physicsObject.Position.X) > Math.Abs(m_grabber.GoalPosition.X * 0.95f) && Math.Abs(m_physicsObject.Position.X) < Math.Abs(m_grabber.GoalPosition.X * 1.05f) &&
                        //    Math.Abs(m_physicsObject.Position.Y) > Math.Abs(m_grabber.GoalPosition.Y * 0.95f) && Math.Abs(m_physicsObject.Position.Y) < Math.Abs(m_grabber.GoalPosition.Y * 1.05f) &&
                        //    Math.Abs(m_physicsObject.Position.Z) > Math.Abs(m_grabber.GoalPosition.Z * 0.95f) && Math.Abs(m_physicsObject.Position.Z) < Math.Abs(m_grabber.GoalPosition.Z * 1.05f))
                        //if (((int)(m_physicsObject.Position.X * 100)) == ((int)(m_grabber.GoalPosition.X * 100)) &&
                        //    ((int)(m_physicsObject.Position.Y * 100)) == ((int)(m_grabber.GoalPosition.Y * 100)) &&
                        //    ((int)(m_physicsObject.Position.Z * 100)) == ((int)(m_grabber.GoalPosition.Z * 100)))
                        //{
                        //    m_grabber.ReleaseLinear();
                        //    m_bPositionSet = false;
                        //}

                        //if (m_physicsObject.LinearVelocity == Vector3.Zero)
                        //{
                        //    m_grabber.ReleaseLinear();
                        //    m_bPositionSet = false;
                        //}

                        Vector3 delta = m_grabber.GoalPosition - m_physicsObject.Position;
                        float length = delta.Length();
                        if (length < m_motionSetting.LinearPositionThreshold)// 0.2f)
                        {
                            if (m_physicsObject.LinearVelocity.Length() < m_motionSetting.LinearVelocityThreshold)// 0.1f)
                            {
                                m_grabber.ReleaseLinear();
                                m_bPositionSet = false;
                            }
                        }
                        else
                        {
                            // Release blocked objects
                            if (m_physicsObject.LinearVelocity.Length() < 0.2f && (m_lastPosition - m_physicsObject.Position).Length() < 0.1f)
                            {
                                m_grabber.ReleaseLinear();
                                m_bPositionSet = false;
                            }
                        }
                    }
                }
                
                m_lastPosition = m_physicsObject.Position;
                //else
                //    m_lastPosition = m_physicsObject.Position;

                // If the object hasn't rotated (because the path is maybe blocked by other objects), release it early than usual
                if (m_bOrientationSet)
                {
                    //if (m_physicsObject.Orientation == m_lastOrientation)
                    //{
                    //    m_grabber.ReleaseAngular();
                    //    m_bOrientationSet = false;
                    //}
                    //else
                    {
                        //if (Math.Abs(m_physicsObject.Orientation.X) > Math.Abs(m_grabber.GoalOrientation.X * 0.95f) && Math.Abs(m_physicsObject.Orientation.X) < Math.Abs(m_grabber.GoalOrientation.X * 1.05f) &&
                        //    Math.Abs(m_physicsObject.Orientation.Y) > Math.Abs(m_grabber.GoalOrientation.Y * 0.95f) && Math.Abs(m_physicsObject.Orientation.Y) < Math.Abs(m_grabber.GoalOrientation.Y * 1.05f) &&
                        //    Math.Abs(m_physicsObject.Orientation.Z) > Math.Abs(m_grabber.GoalOrientation.Z * 0.95f) && Math.Abs(m_physicsObject.Orientation.Z) < Math.Abs(m_grabber.GoalOrientation.Z * 1.05f) &&
                        //    Math.Abs(m_physicsObject.Orientation.W) > Math.Abs(m_grabber.GoalOrientation.W * 0.95f) && Math.Abs(m_physicsObject.Orientation.W) < Math.Abs(m_grabber.GoalOrientation.W * 1.05f))
                        //{
                        //    m_grabber.ReleaseAngular();
                        //    m_bOrientationSet = false;
                        //}

                        //if (m_physicsObject.AngularVelocity == Vector3.Zero)
                        //{
                        //    m_grabber.ReleaseAngular();
                        //    m_bOrientationSet = false;
                        //}

                        Quaternion delta = m_grabber.GoalOrientation - m_physicsObject.Orientation;
                        float length = delta.Length();

                        if (length < m_motionSetting.AngularPositionThreshold)// 0.2f)
                        {
                            if (m_physicsObject.AngularVelocity.Length() < m_motionSetting.AngularVelocityThreshold)// 0.1f)
                            {
                                m_grabber.ReleaseAngular();
                                m_bOrientationSet = false;
                            }
                        }
                        else
                        {
                            // Release blocked objects
                            if (m_physicsObject.AngularVelocity.Length() < 0.02f && (m_lastOrientation - m_physicsObject.Orientation).Length() < 0.01f)
                            {
                                m_grabber.ReleaseLinear();
                                m_bOrientationSet = false;
                            }
                        }
                    }
                }
                m_lastOrientation = m_physicsObject.Orientation;
                //Console.WriteLine(m_grabber.GrabbedPosition.ToString() + " | " + Position.ToString() + " | " + Grabber.GoalPosition.ToString());

                //if (bShouldRelease)
                //    m_grabber.Release();
            }
        }

        /// <summary>
        /// Renders the bounding box of the object.
        /// </summary>
        public void RenderBoundingBox()
        {
            BoundingBox box = new BoundingBox(Model.MinimumBoundingBox.Min, Model.MinimumBoundingBox.Max);
            Vector3[] corners = box.GetCorners();
            for (int i = 0; i < corners.Length; i++)
                Vector3.Transform(ref corners[i], ref m_worldMatrix, out corners[i]);

            DebugShapeRenderer.AddBoundingBox(corners, Color.Red, 0);
        }
                
        #endregion
        
        #region Update & Draw

        /// <summary>
        /// Calculates the world matrix.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            m_worldMatrix = Matrix.CreateScale(Scale) * Matrix.CreateFromQuaternion(Orientation) * Matrix.CreateTranslation(Position);
        }


        /// <summary>
        /// Renders the object using the render delegate.
        /// </summary>
        /// <param name="world"></param>
        /// <param name="bEnableLight"></param>
        /// <param name="camera"></param>
        public virtual void Draw(Matrix world, bool bEnableLight, CameraObject camera)
        {
            //RenderDelegate = delegate
            //{
            //    State.Device.SetVertexBuffer(((PrimitiveModel)m_model).CustomMesh.VertexBuffer);
            //    State.Device.Indices = ((PrimitiveModel)m_model).CustomMesh.IndexBuffer;
            //    State.Device.DrawIndexedPrimitives(((PrimitiveModel)m_model).CustomMesh.PrimitiveType,
            //        0, 0, ((PrimitiveModel)m_model).CustomMesh.NumberOfVertices, 0, ((PrimitiveModel)m_model).CustomMesh.NumberOfPrimitives);
            //};
            
            //m_shader.Render(world, null, RenderDelegate, bEnableLight);

            RenderDelegate();
        }

        public SceneObject Clone()
        {
            // Clone the basic project
            SceneObject newObj = this.MemberwiseClone() as SceneObject;
            
            // Create a new physics and a new grabber, since the "old copies" are already added to a space
            newObj.CreatePhysicsObject();
            newObj.m_grabber = new MotorizedGrabSpring(); 

            // Assign a new ID
            newObj.m_iID = Core.GetNextNodeID();

            // Assign a new name
            newObj.m_strName = this.GetType().ToString() + newObj.m_iID.ToString();

            return newObj;
        }

        #endregion

        #region Protected Methods

        protected virtual void CreatePhysicsObject()
        {

        }

        #endregion

        public void Dispose()
        {
            //m_shader.Dispose();
        }

        #region Private Methods

        private void Events_CreatingContact(EntityCollidable sender, Collidable other, CollidablePairHandler pair, Contact contact)
        {
            if (this.collided != null)
            {
                CollisionArgs e = new CollisionArgs();

                // Find the collider
                if (pair.EntityA == this.m_physicsObject)
                {
                    if (this.scene.CollidersList.ContainsKey(pair.EntityB))
                    {
                        e.Collider = this.scene.CollidersList[pair.EntityB];
                    }
                }
                else
                {
                    if (this.scene.CollidersList.ContainsKey(pair.EntityA))
                    {
                        e.Collider = this.scene.CollidersList[pair.EntityA];
                    }
                }

                e.Contact = contact;

                this.collided(this, e);
            }
        }

        #endregion
    }

    /// <summary>
    /// Creates a box object for the scene.
    /// </summary>
    public class BoxObject : SceneObject
    {
        public override Vector3 Scale
        {
            get
            {
                return base.Scale;
            }
            set
            {
                base.Scale = value;
            }
        }

        #region Fields

        private Vector3 position = Vector3.Zero;
        private Vector3 length = Vector3.Zero;
        float mass = 0f;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a camera object for the scene.
        /// </summary>
        /// <param name="position">Defines the initial box position.</param>
        /// <param name="radius">Defines the initial lengths of the box edges.</param>
        /// <param name="mass">Defines the initial mass of the cylinder.</param>
        public BoxObject(Vector3 position, Vector3 length, float fMass) : base()
        {
            NOVA.Graphics.Box box = new NOVA.Graphics.Box(length);
            Model = box;
            //Model.Shader = m_shader;

            this.position = position;
            this.length = length;
            this.mass = fMass;

            Type = "BoxObject";
            ShapeParameters = box.ShapeProperties;

            this.CreatePhysicsObject();

            Name = "Box";

            //RenderDelegate = delegate
            //    {
            //        State.Device.SetVertexBuffer(box.CustomMesh.VertexBuffer);
            //        State.Device.Indices = box.CustomMesh.IndexBuffer;
            //        State.Device.DrawIndexedPrimitives(box.CustomMesh.PrimitiveType,
            //            0, 0, box.CustomMesh.NumberOfVertices, 0, box.CustomMesh.NumberOfPrimitives);
            //    };
            
            base.Position = position;
        }
        #endregion

        protected override void CreatePhysicsObject()
        {
            // Prevent internal BEPU physics engine exceptions, when trying 
            // to make an object with zero mass dynamic
            if (this.mass > 0)
            {
                Physics = new BEPUphysics.Entities.Prefabs.Box(this.position, this.length.X, this.length.Y, this.length.Z, this.mass);
            }
            else
            {
                Physics = new BEPUphysics.Entities.Prefabs.Box(this.position, this.length.X, this.length.Y, this.length.Z);
            }
        }
    }

    /// <summary>
    /// Creates a sphere object for the scene.
    /// </summary>
    public class SphereObject : SceneObject
    {
        // Revision 2

        #region Members
        private Vector3 position = Vector3.Zero;
        private float radius = 0f;
        private float mass = 0f;
        #endregion

        #region Properties

        /// <summary>
        /// Gets the radius of the sphere.
        /// </summary>
        public float Radius
        {
            get { return this.radius; }
        }
        #endregion

        #region Constructors

        /// <summary>
        /// Creates a sphere object for the scene.
        /// </summary>
        /// <param name="position">Defines the initial sphere position.</param>
        /// <param name="radius">Defines the initial sphere radius.</param>
        /// <param name="slices">Defines the amount of slices in which the horizontal perimeter of the cylinder is divided. Use a relatively small number like 10 to avoid performance issues. This has to be greater than 4 and less than 101.</param>
        /// <param name="stacks">Defines the amount of slices in which the vertical perimeter of the cylinder is divided. Use a relatively small number like 10 to avoid performance issues. This has to be greater than 4 and less than 101.</param>
        /// <param name="mass">Defines the initial mass of the cylinder.</param>
        public SphereObject(Vector3 position, float radius, int slices, int stacks, float fMass)
            : base()
        {
            this.position = position;
            this.radius = radius;
            this.mass = fMass;

            NOVA.Graphics.Sphere sphere = new NOVA.Graphics.Sphere(radius, slices, stacks);
            Model = sphere;

            Type = "SphereObject";
            ShapeParameters = sphere.ShapeProperties;

            this.CreatePhysicsObject();

            Name = "Sphere";

            //RenderDelegate = delegate
            //{
            //    State.Device.SetVertexBuffer(sphere.CustomMesh.VertexBuffer);
            //    State.Device.Indices = sphere.CustomMesh.IndexBuffer;
            //    State.Device.DrawIndexedPrimitives(sphere.CustomMesh.PrimitiveType,
            //        0, 0, sphere.CustomMesh.NumberOfVertices, 0, sphere.CustomMesh.NumberOfPrimitives);
            //};

            base.Position = position;
        }
        #endregion

        protected override void CreatePhysicsObject()
        {
            // Prevent internal BEPU physics engine exceptions, when trying 
            // to make an object with zero mass dynamic
            if (this.mass > 0)
            {
                Physics = new BEPUphysics.Entities.Prefabs.Sphere(this.position, this.radius, this.mass);
            }
            else
            {
                Physics = new BEPUphysics.Entities.Prefabs.Sphere(this.position, this.radius);
            }
        }
    }

    /// <summary>
    /// Creates a cylinder object for the scene.
    /// </summary>
    public class CylinderObject : SceneObject
    {
        // Revision 3

        public override Vector3 Scale
        {
            get
            {
                return this.m_scale;
            }
            set
            {
                if (this.m_scale.X != value.X)
                {
                    (this.Physics as Cylinder).Radius = value.X;
                    this.m_scale = new Vector3(value.X, this.m_scale.Z, value.X);
                }
                else if (base.Scale.Y != value.Y)
                {
                    (this.Physics as Cylinder).Height = value.Y;
                    this.m_scale = new Vector3(value.Y, this.m_scale.Z, value.Y);
                }
                else if (base.Scale.Z != value.Z)
                {
                    (this.Physics as Cylinder).Radius = value.Z;
                    this.m_scale = new Vector3(this.m_scale.X, value.Z, this.m_scale.Z);
                }

                Helpers.ReactivatePhysicsObject(this.Physics);
            }
        }

        #region Members

        private Vector3 position = Vector3.Zero;
        private float radius = 0f;
        private float height = 0f;
        private float mass = 0f;

        #endregion

        #region Properties

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a cylinder object for the scene.
        /// </summary>
        /// <param name="position">Defines the initial cylinder position.</param>
        /// <param name="radius">Defines the initial cylinder radius.</param>
        /// <param name="height">Defines the initial cylinder height.</param>
        /// <param name="slices">Defines the amount of slices in which the perimeter of the cylinder is divided. Use a relatively small number like 10 to avoid performance issues.  This has to be greater than 4 and less than 101.</param>
        /// <param name="mass">Defines the initial mass of the cylinder.</param>
        public CylinderObject(Vector3 position, float radius, float height, int slices, float mass)
            : base()
        {
            this.position = position;
            this.radius = radius;
            this.height = height;
            this.mass = mass;

            Model = CreateCylinder(radius, height, slices, mass);// new GoblinXNA.Graphics.Geometry.Cylinder(radius, radius, height, slices);

            Type = "CylinderObject";

            this.CreatePhysicsObject();

            Name = "Cylinder";

            base.Position = position;
        }
        #endregion

        #region Internal Methods

        protected override void CreatePhysicsObject()
        {
            // Prevent internal BEPU physics engine exceptions, when trying 
            // to make an object with zero mass dynamic
            if (this.mass > 0)
            {
                Physics = new BEPUphysics.Entities.Prefabs.Cylinder(this.position, this.height, this.radius, this.mass);
            }
            else
            {
                Physics = new BEPUphysics.Entities.Prefabs.Cylinder(this.position, this.height, this.radius);
            }
        }

        /// <summary>
        /// Create a "custom" cone with an additional vertical offset using the modified default GoblinXNA code
        /// </summary>
        internal PrimitiveModel CreateCylinder(float radius, float height, float slices, float verticalOffset)
        {
            CustomMesh mesh = new CustomMesh();

            NormalMappedVertex[] vertices = new NormalMappedVertex[(int)(slices+1)*4 + 2];

            // Add top center vertex
            NormalMappedVertex topCenter = new NormalMappedVertex();
            topCenter.Position = new Vector3(0, height / 2, 0);
            topCenter.Normal = Vector3.UnitY;
            topCenter.TexCoord = new Vector2(0.5f, 0.5f);
            topCenter.Tangent = Vector4.UnitX;

            vertices[0] = topCenter;

            // Add bottom center vertex
            NormalMappedVertex bottomCenter = new NormalMappedVertex();
            bottomCenter.Position = new Vector3(0, -height / 2, 0);
            bottomCenter.Normal = -Vector3.UnitY;
            bottomCenter.TexCoord = new Vector2(0.5f, 0.5f);
            bottomCenter.Tangent = Vector4.UnitX;

            vertices[1] = bottomCenter;

            double angle = 0;
            double incr = Math.PI * 2 / slices;
            float cos, sin;
            Vector3 u, v;
            Matrix mat;
            float rotAngle = (float)Math.Atan(radius / height);
            Vector3 down = -Vector3.UnitY;

            int index = 2; // First two vertices are already set

            // Add top & bottom side vertices
            for (int i = 0; i <= slices; i++, angle += incr)
            {
                cos = (float)Math.Cos(angle);
                sin = (float)Math.Sin(angle);

                NormalMappedVertex topSide = new NormalMappedVertex();
                topSide.Position = new Vector3(cos * radius, height / 2, sin * radius);
                topSide.Normal = Vector3.Normalize(topSide.Position - topCenter.Position);
                topSide.TexCoord = new Vector2(1f / slices * i, 0f);

                NormalMappedVertex topSide2 = new NormalMappedVertex();
                topSide2.Position = new Vector3(cos * radius, height / 2, sin * radius);
                topSide2.Normal = topCenter.Normal;
                topSide2.TexCoord = new Vector2(1f / slices * i, 0f);

                // Add bottom side vertices
                NormalMappedVertex bottomSide = new NormalMappedVertex();
                bottomSide.Position = new Vector3(cos * radius, -height / 2, sin * radius);
                bottomSide.Normal = Vector3.Normalize(bottomSide.Position - bottomCenter.Position);
                bottomSide.TexCoord = new Vector2(1f / slices * i, 1f);

                NormalMappedVertex bottomSide2 = new NormalMappedVertex();
                bottomSide2.Position = new Vector3(cos * radius, -height / 2, sin * radius);
                bottomSide2.Normal = bottomCenter.Normal;
                bottomSide2.TexCoord = new Vector2(1f / slices * i, 1f);

                v = topSide.Normal;
                u = Vector3.Cross(v, down);
                mat = Matrix.CreateTranslation(v) * Matrix.CreateFromAxisAngle(u, -rotAngle);
                topSide.Normal = bottomSide.Normal = Vector3.Normalize(mat.Translation);                

                vertices[index] = topSide;
                vertices[index + 1] = topSide2;
                vertices[index + 2] = bottomSide;
                vertices[index + 3] = bottomSide2;

                index += 4;
            }

            mesh.VertexDeclaration = NormalMappedVertex.VertexDeclaration;

            mesh.VertexBuffer = new VertexBuffer(Core.Device, typeof(NormalMappedVertex), vertices.Length, BufferUsage.None);
            mesh.VertexBuffer.SetData(vertices);


            List<short> indices = new List<short>();
                        
            // Create top & bottom circle 
            for (int i = 2; i < vertices.Length - 7; i += 4)
            {
                indices.Add(0);
                indices.Add((short)(i + 1));
                indices.Add((short)(i + 5));

                indices.Add(1);
                indices.Add((short)(i + 7));
                indices.Add((short)(i + 3));
            }

            // Create side
            for (int i = 2; i < vertices.Length - 7; i += 4)
            {
                indices.Add((short)i);
                indices.Add((short)(i + 2));
                indices.Add((short)(i + 4));

                indices.Add((short)(i + 4));
                indices.Add((short)(i + 2));
                indices.Add((short)(i + 6));
            }


            //// Calculate the tangent space basis vectors for the cube faces.
            //Vector4 tangent;
            //Vector3 binormal;

            //for (int i = 0; i < indices.Count; i += 3)//for (int i = 2; i < vertices.Length; i += 3)
            //{
            //    //if (i + 2 < vertices.Length)
            //    {
            //        NormalMappedVertex.CalcTangent(
            //            ref vertices[indices[i]].Position,
            //            ref vertices[indices[i + 1]].Position,
            //            ref vertices[indices[i + 2]].Position,
            //            ref vertices[indices[i]].TexCoord,
            //            ref vertices[indices[i + 1]].TexCoord,
            //            ref vertices[indices[i + 2]].TexCoord,
            //            ref vertices[indices[i]].Normal,
            //            out tangent,
            //            out binormal);

            //        vertices[indices[i]].Tangent = tangent;
            //        vertices[indices[i + 1]].Tangent = tangent;
            //        vertices[indices[i + 2]].Tangent = tangent;

            //        vertices[indices[i]].Binormal = binormal;
            //        vertices[indices[i + 1]].Binormal = binormal;
            //        vertices[indices[i + 2]].Binormal = binormal;                    
            //    }
            //}


            mesh.IndexBuffer = new IndexBuffer(Core.Device, typeof(short), indices.Count, BufferUsage.None);
            mesh.IndexBuffer.SetData(indices.ToArray());

            mesh.NumberOfVertices = vertices.Length;
            mesh.NumberOfPrimitives = indices.Count / 3;

            return new PrimitiveModel(mesh);
        }
        #endregion
    }

    /// <summary>
    /// Creates a capsule object for the scene.
    /// </summary>
    public class CapsuleObject : SceneObject
    {
        // Revision 3

        #region Members
        private Vector3 position = Vector3.Zero;
        private float radius = 0f;
        private float height = 0f;
        private float mass = 0f;
        #endregion

        #region Properties

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a capsule object for the scene.
        /// </summary>
        /// <param name="position">Defines the initial capsule position.</param>
        /// <param name="radius">Defines the initial capsule radius.</param>
        /// <param name="height">Defines the initial capsule height.</param>
        /// <param name="slices">Defines the amount of slices in which the perimeter of the capsule is divided. Use a relatively small number like 10 to avoid performance issues.  This has to be greater than 4 and less than 101.</param>
        /// <param name="mass">Defines the initial mass of the capsule.</param>
        public CapsuleObject(Vector3 position, float radius, float height, int slices, float fMass)
            : base()
        {
            this.position = position;
            this.radius = radius;
            this.height = height;
            this.mass = fMass;

            Model = CreateCapsule(radius, height, slices);//new GoblinXNA.Graphics.Geometry.Capsule(radius, height, slices);

            Type = "CapsuleObject";

            this.CreatePhysicsObject();

            Name = "Capsule";

            base.Position = position;
        }
        #endregion

        #region Internal Methods

        protected override void CreatePhysicsObject()
        {
            // Prevent internal BEPU physics engine exceptions, when trying 
            // to make an object with zero mass dynamic
            if (this.mass > 0)
            {
                Physics = new BEPUphysics.Entities.Prefabs.Capsule(this.position, this.height, this.radius, this.mass);
            }
            else
            {
                Physics = new BEPUphysics.Entities.Prefabs.Capsule(this.position, this.height, this.radius);
            }
        }

        /// <summary>
        /// Create a "custom" capsule with an additional vertical offset using the modified default GoblinXNA code
        /// </summary>
        internal PrimitiveModel CreateCapsule(float radius, float height, int slices)
        {
            CustomMesh mesh = new CustomMesh();
            
            float cylinderHeight = height - radius * 2;
            Vector3 topCenter = new Vector3(0, cylinderHeight / 2, 0);
            Vector3 bottomCenter = new Vector3(0, -cylinderHeight / 2, 0);

            double thai = 0, theta = 0;
            double thaiIncr = Math.PI / slices;
            double thetaIncr = Math.PI * 2 / slices;
            int countB, countA;

            NormalMappedVertex[] vertices = new NormalMappedVertex[(int)((Math.PI - (3 * thaiIncr / 2)) / thaiIncr + 0.5f) * slices + 2];

            int index = 0;

            // Add top hemisphere vertices
            for (countA = 0, thai = thaiIncr; thai < Math.PI / 2; thai += thaiIncr, countA++)
            {
                for (countB = 0, theta = 0; countB < slices; theta += thetaIncr, countB++)
                {
                    NormalMappedVertex vert = new NormalMappedVertex();
                    vert.Position = new Vector3((float)(radius * Math.Sin(thai) * Math.Cos(theta)),
                        (float)(radius * Math.Cos(thai)) + cylinderHeight / 2,
                        (float)(radius * Math.Sin(thai) * Math.Sin(theta)));
                    vert.Normal = Vector3.Normalize(vert.Position - topCenter);
                    vertices[index] = vert;
                    vertices[index].TexCoord = new Vector2((1f / slices) * (float)(countB+1), 0.25f*vert.Position.Y/cylinderHeight);// new Vector2(vert.Normal.X / 2f + 0.5f, (vert.Normal.Y / 2f + 0.5f) * (0.5f*vert.Position.Y / (cylinderHeight+radius)) * 0.5f);

                    index++;
                }
            }

            // Add bottom hemisphere vertices
            for (thai = Math.PI / 2; thai < Math.PI - thaiIncr / 2; thai += thaiIncr, countA++)
            {
                for (countB = 0, theta = 0; countB < slices; theta += thetaIncr, countB++)
                {
                    NormalMappedVertex vert = new NormalMappedVertex();
                    vert.Position = new Vector3((float)(radius * Math.Sin(thai) * Math.Cos(theta)),
                        (float)(radius * Math.Cos(thai)) - cylinderHeight / 2,
                        (float)(radius * Math.Sin(thai) * Math.Sin(theta)));
                    vert.Normal = Vector3.Normalize(vert.Position - bottomCenter);                    
                    vertices[index] = vert;
                    vertices[index].TexCoord = new Vector2((1f / slices) * (float)(countB+1), 0.25f*vert.Position.Y / cylinderHeight);// new Vector2(-vert.Normal.X / 2f + 0.5f, (-vert.Normal.Y / 2f + 0.5f) * (0.5f*vert.Position.Y / (cylinderHeight+radius)) * 0.5f);

                    index++;
                }
            }

            // Add north pole vertex
            vertices[index++] = new NormalMappedVertex(new Vector3(0, radius + cylinderHeight / 2, 0), new Vector2(0, 0), Vector3.UnitY, Vector4.UnitX, Vector3.UnitZ);
            // Add south pole vertex
            vertices[index++] = new NormalMappedVertex(new Vector3(0, -radius - cylinderHeight / 2, 0), new Vector2(0, 1), -Vector3.UnitY, Vector4.UnitX, -Vector3.UnitZ);

            mesh.VertexDeclaration = NormalMappedVertex.VertexDeclaration;

            mesh.VertexBuffer = new VertexBuffer(Core.Device, typeof(NormalMappedVertex), vertices.Length, BufferUsage.None);
            mesh.VertexBuffer.SetData(vertices.ToArray());

            List<short> indices = new List<short>();

            // Create the north and south pole area mesh
            for (int i = 0, j = (countA - 1) * slices; i < slices - 1; i++, j++)
            {
                indices.Add((short)(vertices.Length - 2));
                indices.Add((short)i);
                indices.Add((short)(i + 1));

                indices.Add((short)(vertices.Length - 1));
                indices.Add((short)(j + 1));
                indices.Add((short)j);
            }
            indices.Add((short)(vertices.Length - 2));
            indices.Add((short)(slices - 1));
            indices.Add((short)0);

            indices.Add((short)(vertices.Length - 1));
            indices.Add((short)((countA - 1) * slices));
            indices.Add((short)(vertices.Length - 3));

            // Create side of the hemispheres
            for (int i = 0; i < countA - 1; i++)
            {
                for (int j = 0; j < slices - 1; j++)
                {
                    indices.Add((short)(i * slices + j));
                    indices.Add((short)((i + 1) * slices + j));
                    indices.Add((short)((i + 1) * slices + j + 1));

                    indices.Add((short)(i * slices + j));
                    indices.Add((short)((i + 1) * slices + j + 1));
                    indices.Add((short)(i * slices + j + 1));
                }

                indices.Add((short)((i + 1) * slices - 1));
                indices.Add((short)((i + 2) * slices - 1));
                indices.Add((short)((i + 1) * slices));

                indices.Add((short)((i + 1) * slices - 1));
                indices.Add((short)((i + 1) * slices));
                indices.Add((short)(i * slices));
            }

            // Calculate the tangent space basis vectors for the cube faces.
            Vector4 tangent;
            Vector3 binormal;

            for (int i = 0; i < indices.Count; i += 3)//for (int i = 2; i < vertices.Length; i += 3)
            {
                //if (i + 2 < vertices.Length)
                {
                    NormalMappedVertex.CalcTangent(
                        ref vertices[indices[i]].Position,
                        ref vertices[indices[i + 1]].Position,
                        ref vertices[indices[i + 2]].Position,
                        ref vertices[indices[i]].TexCoord,
                        ref vertices[indices[i + 1]].TexCoord,
                        ref vertices[indices[i + 2]].TexCoord,
                        ref vertices[indices[i]].Normal,
                        out tangent,
                        out binormal);

                    vertices[indices[i]].Tangent = tangent;
                    vertices[indices[i + 1]].Tangent = tangent;
                    vertices[indices[i + 2]].Tangent = tangent;

                    vertices[indices[i]].Binormal = binormal;
                    vertices[indices[i + 1]].Binormal = binormal;
                    vertices[indices[i + 2]].Binormal = binormal;
                }
            }

            mesh.IndexBuffer = new IndexBuffer(Core.Device, typeof(short), indices.Count,
                BufferUsage.None);
            mesh.IndexBuffer.SetData(indices.ToArray());

            mesh.NumberOfVertices = vertices.Length;
            mesh.NumberOfPrimitives = indices.Count / 3;

            return new PrimitiveModel(mesh);
        }
        #endregion
    }

    /// <summary>
    /// Creates a cone object for the scene.
    /// </summary>
    public class ConeObject : SceneObject
    {
        // Revision 3

        #region Members

        private Vector3 position = Vector3.Zero;
        private float radius = 0f;
        private float height = 0f;
        private float mass = 0f;

        #endregion

        #region Properties

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a cone object for the scene.
        /// </summary>
        /// <param name="position">Defines the initial cone position.</param>
        /// <param name="radius">Defines the initial cone radius.</param>
        /// <param name="height">Defines the initial cone height.</param>
        /// <param name="slices">Defines the amount of slices in which the perimeter of the cone is divided. Use a relatively small number like 10 to avoid performance issues. This has to be greater than 4 and less than 101.</param>
        /// <param name="mass">Defines the initial mass of the cone.</param>
        public ConeObject(Vector3 position, float radius, float height, int slices, float fMass)
            : base()
        {
            this.position = position;
            this.radius = radius;
            this.height = height;
            this.mass = fMass;

            // Hence there's an internal difference in the BEPU physics engine 
            // between the physical and graphical cone of one forth of the height,
            // this has to be compensated here.
            float offset = (float)height / 4;

            // Create a "custom" cone, compensating the offset
            Model = CreateOffsetCone(radius, height, slices, offset);

            Type = "ConeObject";

            this.CreatePhysicsObject();

            Name = "Cone";

            base.Position = position;

            // All(!) BEPU Cone have a slight vertical offset to object below them
        }
        #endregion

        #region Internal Methods

        protected override void CreatePhysicsObject()
        {
            // Prevent internal BEPU physics engine exceptions, when trying 
            // to make an object with zero mass dynamic
            if (this.mass > 0)
            {
                Physics = new BEPUphysics.Entities.Prefabs.Cone(this.position, this.height, this.radius, this.mass);
            }
            else
            {
                Physics = new BEPUphysics.Entities.Prefabs.Cone(this.position, this.height, this.radius);
            }
        }

        /// <summary>
        /// Create a "custom" cone with an additional vertical offset using the modified default GoblinXNA code
        /// </summary>
        internal PrimitiveModel CreateOffsetCone(float radius, float height, int slices, float verticalOffset)
        {
            CustomMesh mesh = new CustomMesh();

            NormalMappedVertex[] vertices = new NormalMappedVertex[2 + (slices+1)*3];

            // Add top center vertex
            NormalMappedVertex topCenter = new NormalMappedVertex();
            topCenter.Position = new Vector3(0, (height / 2) + verticalOffset, 0);
            topCenter.Normal = Vector3.UnitY;
            topCenter.TexCoord = new Vector2(0.5f, 0.5f);
            topCenter.Tangent = Vector4.UnitX;

            vertices[0] = topCenter;

            // Add bottom center vertex
            NormalMappedVertex bottomCenter = new NormalMappedVertex();
            bottomCenter.Position = new Vector3(0, (-height / 2) + verticalOffset, 0);
            bottomCenter.Normal = -Vector3.UnitY;
            bottomCenter.TexCoord = new Vector2(0.0f, 0.0f);
            bottomCenter.Tangent = Vector4.UnitX;

            vertices[1] = bottomCenter;



            double angle = 0;
            double incr = Math.PI * 2 / slices;
            float cos, sin;
            Vector3 u, v;
            Matrix mat;
            float rotAngle = (float)Math.Atan(radius / height);
            Vector3 down = -Vector3.UnitY;

            int index = 2; // First two indices are already processed
            
            // Add top & bottom side vertices
            for (int i = 0; i <= slices; i++, angle += incr)
            {
                cos = (float)Math.Cos(angle);
                sin = (float)Math.Sin(angle);

                NormalMappedVertex topSide = new NormalMappedVertex();
                topSide.Position = topCenter.Position;
                topSide.TexCoord = topCenter.TexCoord;

                // Add bottom side vertices
                NormalMappedVertex bottomSide = new NormalMappedVertex();
                bottomSide.Position = new Vector3(cos * radius, (-height / 2) + verticalOffset, sin * radius);
                bottomSide.Normal = Vector3.Normalize(bottomSide.Position - bottomCenter.Position);
                bottomSide.TexCoord = new Vector2(1f / slices * i, 1f); 

                NormalMappedVertex bottomSide2 = new NormalMappedVertex();
                bottomSide2.Position = new Vector3(cos * radius, (-height / 2) + verticalOffset, sin * radius);
                bottomSide2.Normal = bottomCenter.Normal;
                bottomSide2.TexCoord = new Vector2(1f / slices * i, 1f); 

                // TO_DELETE
                //v = bottomSide.Normal;
                //u = Vector3.Cross(v, down);
                //mat = Matrix.CreateTranslation(v) * Matrix.CreateFromAxisAngle(u, rotAngle);
                //topSide.Normal = bottomSide.Normal = Vector3.Normalize(mat.Translation);

                vertices[index] = topSide;
                vertices[index + 1] = bottomSide;
                vertices[index + 2] = bottomSide2;

                index += 3;
            }


            mesh.VertexDeclaration = NormalMappedVertex.VertexDeclaration;

            mesh.VertexBuffer = new VertexBuffer(Core.Device, typeof(NormalMappedVertex), vertices.Length, BufferUsage.None);
            mesh.VertexBuffer.SetData(vertices);

            List<short> indices = new List<short>();

            
            // Create bottom circle 
            for (int i = 2; i < vertices.Length - 5; i += 3)
            {
                indices.Add(1);
                indices.Add((short)(i + 5));
                indices.Add((short)(i + 2));
            }

            // Create side
            for (int i = 2; i < vertices.Length - 5; i += 3)
            {
                indices.Add((short)i);
                indices.Add((short)(i + 1));
                indices.Add((short)(i + 4));
            }


            // Calculate the tangent space basis vectors for the cube faces.
            Vector4 tangent;
            Vector3 binormal;

            //for (int i = 0; i < indices.Count; i += 3)//for (int i = 2; i < vertices.Length; i += 3)
            //{
            //    //if (i + 2 < vertices.Length)
            //    {
            //        NormalMappedVertex.CalcTangent(
            //            ref vertices[indices[i]].Position,
            //            ref vertices[indices[i + 1]].Position,
            //            ref vertices[indices[i + 2]].Position,
            //            ref vertices[indices[i]].TexCoord,
            //            ref vertices[indices[i + 1]].TexCoord,
            //            ref vertices[indices[i + 2]].TexCoord,
            //            ref vertices[indices[i]].Normal,
            //            out tangent,
            //            out binormal);

            //        vertices[indices[i]].Tangent = tangent;
            //        vertices[indices[i + 1]].Tangent = tangent;
            //        vertices[indices[i + 2]].Tangent = tangent;

            //        vertices[indices[i]].Binormal = binormal;
            //        vertices[indices[i + 1]].Binormal = binormal;
            //        vertices[indices[i + 2]].Binormal = binormal;
            //    }
            //}


            mesh.IndexBuffer = new IndexBuffer(Core.Device, typeof(short), indices.Count, BufferUsage.None);
            mesh.IndexBuffer.SetData(indices.ToArray());

            mesh.NumberOfVertices = vertices.Length;
            mesh.NumberOfPrimitives = indices.Count / 3;

            return new PrimitiveModel(mesh);
        }
        #endregion
    }

    /// <summary>
    /// Creates a model object for the scene.
    /// </summary>
    public class ModelObject : SceneObject
    {
        // Revision 4

        bool m_bUseInternalMaterials;
        internal Microsoft.Xna.Framework.Graphics.Model modelXNA;
        Effect m_effectDR;
        Matrix[] transforms;
        string m_strAssetName;
        CollisionType m_collisionType;
        

        #region Properties

        /// <summary>
        /// If enabled, the internal material properties of the model are used.
        /// </summary>
        public bool UseInternalMaterials
        {
            get { return m_bUseInternalMaterials; }
            set { m_bUseInternalMaterials = value; }
        }

        public string AssetName
        {
            get { return m_strAssetName; }
        }

        public CollisionType CollisionType
        {
            get { return m_collisionType; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a model object for the scene.
        /// </summary>
        /// <param name="position">Defines the initial model position.</param>
        /// <param name="orientation">Defines the initial model orientation</param>
        /// <param name="scale">Defines the initial model scale.</param>
        /// <param name="collisionType">Defines the model collision mesh type. Choose between BoundingSphere, ConvexHull and ExactMesh.</param>
        /// <param name="strPath">Path of the asset in the content project.</param>
        /// <param name="strAssetName">Asset name of the model in the content project.</param>
        /// <param name="mass">Defines the initial mass of the model.</param>
        public ModelObject(Vector3 position, Quaternion orientation, Vector3 scale, CollisionType collisionType, string strPath, string strAssetName, float mass)
            : base()
        {
            Type = "ModelObject";

            m_bUseInternalMaterials = false;
            m_strAssetName = strAssetName;
            m_collisionType = collisionType;

            // Load the model as "GoblinXNA model type"
            //ModelLoader loader = new ModelLoader();
            modelXNA = Core.Content.Load<Microsoft.Xna.Framework.Graphics.Model>(@"" + Path.Combine(strPath, strAssetName));

            //GoblinXNA.Graphics.Model model = (GoblinXNA.Graphics.Model)loader.Load(strPath, strAssetName);
            if (modelXNA != null)
            {
                transforms = new Matrix[modelXNA.Bones.Count];
                modelXNA.CopyAbsoluteBoneTransformsTo(transforms);

                IModel model = new MeshModel(transforms, modelXNA.Meshes, null);
                //model.UseInternalMaterials = true;
                Model = model;
                //Model.Shader = m_shader;
            }

            m_effectDR = Core.RenderGBuffer;//State.Content.Load<Effect>("RenderGBuffer");

            // Load the model as normal model for BEPU physics by loading the mesh data and creating the group.
            Vector3[] vertices;
            int[] indices;
            //modelXNA = Core.Content.Load<Microsoft.Xna.Framework.Graphics.Model>(System.IO.Path.Combine(strPath, strAssetName));

            // This load method wraps the TriangleMesh.GetVerticesAndIndicesFromModel method 
            // to output vertices of type StaticTriangleGroupVertex instead of TriangleMeshVertex or simply Vector3.
            TriangleMesh.GetVerticesAndIndicesFromModel(modelXNA, out vertices, out indices);

            var transform = new AffineTransform(scale, Quaternion.Identity, Vector3.Zero);

            Vector3 center = Vector3.Zero;
            
            // Create the collision mesh depending on the specified type
            switch (collisionType)
            {
                case CollisionType.ExactMesh:
                    ShapeDistributionInformation shapeInfo;
                    MobileMeshShape mobileShape = new MobileMeshShape(vertices, indices, transform, MobileMeshSolidity.Counterclockwise, out shapeInfo);

                    center = shapeInfo.Center;

                    if (mass > 0f)
                    {
                        Physics = new Entity(mobileShape, mass);
                        //Physics.BecomeDynamic(mass);
                    }
                    else
                        Physics = new Entity(mobileShape);
                    break;
                case CollisionType.ConvexHull:

                    ConvexHullShape convexShape = new ConvexHullShape(vertices, out center);

                    if (mass > 0f)
                        Physics = new Entity(convexShape, mass);
                    else
                        Physics = new Entity(convexShape);
                    break;
                case NOVA.CollisionType.BoundingSphere:

                    float radius = 0f;

                    foreach (ModelMesh mesh in modelXNA.Meshes)
                        if (mesh.BoundingSphere.Radius > radius)
                            radius = mesh.BoundingSphere.Radius;

                    SphereShape sphere = new SphereShape(radius);

                    if (mass > 0f)
                        Physics = new Entity(sphere, mass);
                    else
                        Physics = new Entity(sphere);
                    break;
            }

            // THIS IS IMPORTANT: 
            // The position of the collision mesh is represented by the center ofmass,
            // which normally doesn't match the graphics mesh center. Therefore the 
            // initial offset between physical and graphical mesh must be saved and
            // adapted to the graphical mesh during the rendering.
            //m_graphicsPhysOffset = mobileMesh.Position;
            m_graphicsPhysOffset = center;

            Name = "StaticModel";

            base.Position = position;
            
            base.Orientation = orientation;
            base.Scale = scale;
        }
        #endregion

        #region Public Method

        /// <summary>
        /// Renders the model object.
        /// </summary>
        public override void Draw(Matrix world, bool bEnableLight, CameraObject camera)
        {
            //if (RenderType == GoblinXNA_Derivatives.RenderType.Simple)
            {
                foreach (ModelMesh mesh in modelXNA.Meshes)
                {
                    //m_effectDR.Parameters["World"].SetValue(world);
                    //m_effectDR.Parameters["View"].SetValue(camera.ViewMatrix);
                    //m_effectDR.Parameters["Projection"].SetValue(camera.ProjectionMatrix);

                    //m_effectDR.Parameters["cameraPos"].SetValue(camera.Position);

                    //m_effectDR.Parameters["Texture"].SetValue(obj.RenderMaterial.Texture);
                    //m_effectDR.Parameters["NormalMap"].SetValue(obj.RenderMaterial.NormalMap);
                    //m_effectDR.Parameters["HeightMap"].SetValue(obj.RenderMaterial.HeightMap);

                    //m_effectDR.Parameters["SpecularMap"].SetValue(obj.RenderMaterial.SpecularMap);


                    //m_effectDR.Parameters["scaleBias"].SetValue(new Vector2(0.04f, -0.03f));

                    //m_effectDR.Parameters["diffuseColor"].SetValue(obj.RenderMaterial.Diffuse);

                    //m_effectDR.Parameters["textureSet"].SetValue((obj.RenderMaterial.Texture != null));



                    // THIS IS IMPORTANT:
                    // Adapt the graphical mesh to the collision mesh using the intial offset
                    Matrix newWorld = Matrix.CreateTranslation(-m_graphicsPhysOffset) * transforms[mesh.ParentBone.Index] * world;

                    modelXNA.CopyAbsoluteBoneTransformsTo(transforms);

                    foreach (Effect effect in mesh.Effects)//m_effectDR.CurrentTechnique.Passes)
                    {
                        if (effect is BasicEffect)
                        {
                            (effect as BasicEffect).World = newWorld;
                            (effect as BasicEffect).View = camera.ViewMatrix;
                            (effect as BasicEffect).Projection = camera.ProjectionMatrix;

                            (effect as BasicEffect).EnableDefaultLighting();

                            if (!m_bUseInternalMaterials)
                            {
                                (effect as BasicEffect).DiffuseColor = new Vector3(m_renderMat.Diffuse.X, m_renderMat.Diffuse.Y, m_renderMat.Diffuse.Z);
                                (effect as BasicEffect).SpecularColor = new Vector3(m_renderMat.Specular.X, m_renderMat.Specular.Y, m_renderMat.Specular.Z);

                                if (m_renderMat.TextureSet)
                                {
                                    (effect as BasicEffect).Texture = m_renderMat.Texture;
                                    (effect as BasicEffect).TextureEnabled = true;
                                }
                            }
                        }
                        else
                        {
                            effect.Parameters["World"].SetValue(newWorld);
                            effect.Parameters["View"].SetValue(camera.ViewMatrix);
                            effect.Parameters["Projection"].SetValue(camera.ProjectionMatrix);

                            effect.Parameters["cameraPos"].SetValue(camera.Position);

                            if (!m_bUseInternalMaterials)
                            {
                                if (m_renderMat.TextureSet)
                                {
                                    effect.Parameters["Texture"].SetValue(m_renderMat.Texture);
                                    effect.Parameters["NormalMap"].SetValue(m_renderMat.NormalMap);
                                    effect.Parameters["HeightMap"].SetValue(m_renderMat.HeightMap);

                                    effect.Parameters["SpecularMap"].SetValue(m_renderMat.SpecularMap);
                                }

                                effect.Parameters["scaleBias"].SetValue(new Vector2(0.04f, -0.03f));

                                effect.Parameters["diffuseColor"].SetValue(m_renderMat.Diffuse);

                                effect.Parameters["textureSet"].SetValue((m_renderMat.Texture != null));
                            }
                            else
                            {
                                BasicEffect basicEffect = effect as BasicEffect;

                                effect.Parameters["Texture"].SetValue(basicEffect.Texture);                                

                                effect.Parameters["scaleBias"].SetValue(new Vector2(0.04f, -0.03f));

                                effect.Parameters["diffuseColor"].SetValue(basicEffect.DiffuseColor);

                                effect.Parameters["textureSet"].SetValue((basicEffect.Texture != null));
                            }
                        }

                        //be.World = Matrix.CreateTranslation(-m_graphicsPhysOffset) * transforms[mesh.ParentBone.Index] * world;
                        //be.View = camera.ViewMatrix;
                        //be.Projection = camera.ProjectionMatrix;

                       

                        //m_effectDR.Parameters["World"].SetValue(world);
                        //m_effectDR.Parameters["View"].SetValue(camera.ViewMatrix);
                        //m_effectDR.Parameters["Projection"].SetValue(camera.ProjectionMatrix);

                        //m_effectDR.Parameters["cameraPos"].SetValue(camera.Position);

                        //m_effectDR.Parameters["Texture"].SetValue(be.Texture);

                        //m_effectDR.CurrentTechnique.Passes[0].Apply();

                        //if (bEnableLight)
                        //be.EnableDefaultLighting();

                        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                        {
                            pass.Apply();

                            mesh.Draw();
                        }
                    }
                }
            }
            //else if (RenderType == GoblinXNA_Derivatives.RenderType.DeferredRenderer)
            //{

            //}
        }
        #endregion

        protected override void CreatePhysicsObject()
        {
            throw new NotImplementedException(); // TODO
        }
    }

    /// <summary>
    /// Creates a compound object for the scene.
    /// </summary>
    public class CompoundObject : SceneObject
    {
        // Revision 1

        #region Members
        internal CompoundBody m_compoundbody;
        List<SceneObject> m_children;
        BoundingBox m_boundingBox;
        #endregion

        #region Properties
        public List<SceneObject> Children
        {
            get { return m_children; }
        }

        public override BoundingBox BoundingBox
        {
            get { return new BoundingBox(m_boundingBox.Min + m_compoundbody.Position, m_boundingBox.Max + m_compoundbody.Position); }
        }
        #endregion

        #region Constructor

        /// <summary>
        /// Creates a compound object for the scene
        /// </summary>
        /// <param name="children">List of scene objects, which should be added to the compound object.</param>
        public CompoundObject(List<SceneObject> children, float mass)
            : base()
        {
            m_children = children;

            var bodies = new List<BEPUphysics.CollisionShapes.CompoundShapeEntry>();

            Vector3 min = children[0].BoundingBox.Min;
            Vector3 max = children[0].BoundingBox.Max;

            foreach (SceneObject obj in children)
            {
                RigidTransform transformation = new RigidTransform(obj.Position, obj.Orientation);
                bodies.Add(new BEPUphysics.CollisionShapes.CompoundShapeEntry(obj.Physics.CollisionInformation.Shape, transformation));

                // Calculate the bounding box of the compound object
                if (obj.BoundingBox.Min.X < min.X)
                    min.X = obj.BoundingBox.Min.X;

                if (obj.BoundingBox.Min.Y < min.Y)
                    min.Y = obj.BoundingBox.Min.Y;

                if (obj.BoundingBox.Min.Z < min.Z)
                    min.Z = obj.BoundingBox.Min.Z;

                if (obj.BoundingBox.Max.X > max.X)
                    max.X = obj.BoundingBox.Max.X;

                if (obj.BoundingBox.Max.Y > max.Y)
                    max.Y = obj.BoundingBox.Max.Y;

                if (obj.BoundingBox.Max.Z > max.Z)
                    max.Z = obj.BoundingBox.Max.Z;
            }

            m_boundingBox = new BoundingBox(min, max);

            if(mass != 0)
                m_compoundbody = new CompoundBody(bodies, mass);
            else
                m_compoundbody = new CompoundBody(bodies);

            Physics = m_compoundbody;
        }
        #endregion

        #region Public Methods
        //public override void Draw(Matrix world, bool bEnableLight, CameraObject camera)
        //{
        //    foreach(SceneObject obj in m_children)
        //        obj.dr
        //}
        #endregion

        protected override void CreatePhysicsObject()
        {
            throw new NotImplementedException(); // TODO
        }
    }
}
